package net.alteiar.images.metadata;

import java.awt.Point;
import java.util.Observer;

import net.alteiar.Array2D;

public class VisionInfo {
	private final IImageInfo blocks;
	private final int visionPower;
	private final int maxVision;

	private final EntityInfo entity;

	public VisionInfo(EntityInfo entity, IImageInfo blocks) {
		super();
		this.entity = entity;
		this.blocks = blocks;

		visionPower = (int) (6.5 * 30);
		maxVision = 8 * 30;
	}

	public synchronized void addObserver(Observer o) {
		this.entity.addObserver(o);
	}

	public synchronized void deleteObserver(Observer o) {
		this.entity.deleteObserver(o);
	}

	public EntityInfo getEntity() {
		return this.entity;
	}

	public void computeVision(Array2D image, int visibleColor, int hiddenColor) {
		Point position = entity.getCenter();
		int squareX = position.x / blocks.getSquareSize();
		int squareY = position.y / blocks.getSquareSize();

		int visionSquare = maxVision / blocks.getSquareSize();

		int minX = Math.max(0, squareX - visionSquare);
		int maxX = Math.min(blocks.getImageWidth() - 1, squareX + visionSquare);

		int minY = Math.max(0, squareY - visionSquare);
		int maxY = Math
				.min(blocks.getImageHeight() - 1, squareY + visionSquare);

		for (int x = minX; x < maxX; ++x) {
			line(image, position, new Point(x, minY), visionPower, visibleColor);
			line(image, position, new Point(x, maxY), visionPower, visibleColor);
		}

		for (int y = minY; y < maxY; ++y) {
			line(image, position, new Point(minX, y), visionPower, visibleColor);
			line(image, position, new Point(maxX, y), visionPower, visibleColor);
		}
		line(image, position, new Point(maxX, maxY), visionPower, visibleColor);
	}

	private void addVisible(Array2D image, int squareSize, int x, int y,
			int visibleColor) {
		int realX = x * squareSize;
		int realY = y * squareSize;

		for (int k = 0; k < squareSize; ++k) {
			for (int k1 = 0; k1 < squareSize; ++k1) {
				image.set(realX + k, realY + k1, visibleColor);
			}
		}
	}

	public void line(Array2D image, Point begin, Point end, int currentVision,
			int visibleColor) {
		int w = end.x - begin.x;
		int h = end.y - begin.y;
		int dx1 = 0, dy1 = 0, dx2 = 0, dy2 = 0;
		if (w < 0)
			dx1 = -1;
		else if (w > 0)
			dx1 = 1;
		if (h < 0)
			dy1 = -1;
		else if (h > 0)
			dy1 = 1;
		if (w < 0)
			dx2 = -1;
		else if (w > 0)
			dx2 = 1;
		int longest = Math.abs(w);
		int shortest = Math.abs(h);
		if (!(longest > shortest)) {
			longest = Math.abs(h);
			shortest = Math.abs(w);
			if (h < 0)
				dy2 = -1;
			else if (h > 0)
				dy2 = 1;
			dx2 = 0;
		}

		int x = begin.x;
		int y = begin.y;
		int numerator = longest >> 1;

		int prevX = x;
		int prevY = y;

		int cost = 0;
		double total = 0;
		for (int i = 0; i <= longest; i++) {
			addVisible(image, blocks.getSquareSize(), x, y, visibleColor);

			if (blocks.getValue(x, y) == Integer.MAX_VALUE) {
				return;
			}

			int diff = Math.abs(x - prevX) + Math.abs(y - prevY);
			cost += blocks.getValue(x, y) * diff;
			total += diff;

			if (total > 0) {
				prevX = x;
				prevY = y;

				double avg = cost / total;
				double dist = new Point(x, y).distance(begin) * avg;

				if (dist > currentVision) {
					return;
				}
			}

			numerator += shortest;
			if (!(numerator < longest)) {
				numerator -= longest;
				x += dx1;
				y += dy1;
			} else {
				x += dx2;
				y += dy2;
			}
		}
	}
}
